#pragma once

#include "serialise\serialise.h"
#include "serialise\serialisetypes.h"
#include <fstream>

namespace Json
{

class JObject;
class JArray;
class Deserialiser;

class Serialiser : public Serialisation::Serialiser<Serialiser>
{
public:
	typedef Deserialiser Deserialiser;

	Serialiser(std::ofstream& file) : m_file(file), m_tabs(0)
	{
		BeginObject();
	}

	~Serialiser()
	{
		EndObject();
	}

	template <typename T>
	Serialiser& Serialise(const char* n, const T& v)
	{
		if (m_objects.back()) m_file << ",\n";
		BeginLine();
		m_file << "\"" << n << "\": ";
		*this & const_cast<T&>(v);
		m_objects.back() = true;
		return *this;
	}

	template <typename T>
	void ArrayElement(T& p)
	{
		if (m_objects.back()) m_file << ",\n";
		BeginLine();
		*this & p;
		m_objects.back() = true;
	}

	template <typename Map, typename T>
	void SerialiseEnum(Map types, int n, T& t)
	{
		for (int i = 0; i < n; i++)
		{
			if (t == types[i].type) 
				String(types[i].name);
		}
	}

	void SerialiseString(std::string& s)
	{
		String(s.c_str());
	}

private:
	void String(const char* s)
	{
		m_file << "\"" << s << "\"";
	}

	template <typename T>
	Serialiser& operator & (T& t)
	{
		using namespace Serialisation;
		using Serialisation::Serialise;
		const SerialisationType type = SerialisationInfo<T>::type;

		if (type == SerialisationType::Enum || type == SerialisationType::String)
		{
			Serialise(*this, t);
		}
		else if (type == SerialisationType::Array)
		{
			BeginArray();
			Serialise(*this, t);
			EndArray();
		}
		else
		{
			BeginObject();
			Serialise(*this, t);
			EndObject();
		}

		return *this;
	}

	template <typename T> 
	Serialiser& operator & (Serialisation::Unquoted_t<T>& v) 
	{
		m_file << v.value; return *this;
	}

	Serialiser& operator & (int v) {m_file << v; return *this;}
	Serialiser& operator & (bool v) {m_file << (v ? "true" : "false"); return *this;}
	Serialiser& operator & (float v) {m_file << v; return *this;}
	Serialiser& operator & (uint8 v) {m_file << (int) v; return *this;}
	Serialiser& operator & (uint16 v) {m_file << v; return *this;}
	Serialiser& operator & (uint32 v) {m_file << v; return *this;}
	Serialiser& operator & (uint64 v) {m_file << v; return *this;}
	Serialiser& operator & (int8 v) {m_file << v; return *this;}
	Serialiser& operator & (int16 v) {m_file << v; return *this;}
	Serialiser& operator & (int64 v) {m_file << v; return *this;}
	Serialiser& operator & (const char* v) {String(v); return *this;}
	Serialiser& operator & (std::string& v) {String(v.c_str()); return *this;}

	void BeginObject() 
	{
		m_objects.push_back(false);
		m_file << "{\n";
		m_tabs++;
	}

	void EndObject()
	{
		if (m_objects.back()) m_file << "\n";
		m_objects.pop_back();
		m_tabs--;
		BeginLine();
		m_file << "}";
	}

	void BeginArray() 
	{
		m_objects.push_back(false);
		m_file << "[\n";
		m_tabs++;
	}

	void EndArray()
	{
		if (m_objects.back()) m_file << "\n";
		m_objects.pop_back();
		m_tabs--;
		BeginLine();
		m_file << "]";
	}

	void BeginLine()
	{
		for (int i = 0; i < m_tabs; i++) m_file << "\t";
	}

	std::ofstream& m_file;
	int m_tabs;
	std::vector<bool> m_objects;
};

class Deserialiser : public Serialisation::Deserialiser<Deserialiser>
{
public:
	typedef Serialiser Serialiser;

	Deserialiser(std::ifstream& file);
	Deserialiser(Ut::SharedPtr<Json::JObject> pObject);

	template <typename T>
	Deserialiser& Serialise(const char* n, const T& v)
	{
		Read(n, const_cast<T&>(v));
		return *this;
	}

	template <typename T>
	void ArrayElement(int i, T& t)
	{
		Read(i, t);
	}

	int ArraySize();

	Json::JObject* GetObject() {return m_objects.back();}

	template <typename Map, typename T>
	void SerialiseEnum(Map types, int n, T& t)
	{
		for (int i = 0; i < n; i++)
		{
			if (m_string == types[i].name) 
				t = types[i].type;
		}
	}

	void SerialiseString(std::string& s)
	{
		s = m_string;
	}

	template <typename T>
	void DeserialiseDirect(T& t)
	{
		using Serialisation::Deserialise;
		Deserialise(*this, t);
	}

private:
	template <typename T>
	void Read(const char* name, T& t)
	{
		using namespace Serialisation;
		using Serialisation::Deserialise;
		const SerialisationType type = SerialisationInfo<T>::type;

		if (type == SerialisationType::Enum || type == SerialisationType::String)
		{
			Read(name, m_string);
			Deserialise(*this, t);
		}
		else if (type == SerialisationType::Array)
		{
			if (BeginArray(name))
			{
				Deserialise(*this, t);
				EndArray();
			}
		}
		else 
		{
			if (BeginObject(name))
			{
				Deserialise(*this, t);
				EndObject();
			}
		}
	}

	template <typename T>
	bool Read(int i, T& t)
	{
		using namespace Serialisation;
		using Serialisation::Deserialise;
		const SerialisationType type = SerialisationInfo<T>::type;

		if (type == SerialisationType::Enum || type == SerialisationType::String)
		{
			Read(i, m_string);
			Deserialise(*this, t);
		}
		else if (type == SerialisationType::Array)
		{
			if (BeginArray(i))
			{
				Deserialise(*this, t);
				EndArray();
				return true;
			}
		}
		else 
		{
			if (BeginObject(i))
			{
				Deserialise(*this, t);
				EndObject();
				return true;
			}
		}

		return false;
	}

	bool BeginObject(const char* name);
	bool BeginObject(int i);
	void EndObject();
	
	bool BeginArray(const char* name);
	bool BeginArray(int i);
	void EndArray();

	void Read(const char* name, int& v);
	void Read(const char* name, bool& v);
	void Read(const char* name, float& v);
	void Read(const char* name, uint8& v);
	void Read(const char* name, uint16& v);
	void Read(const char* name, uint32& v);
	void Read(const char* name, uint64& v);
	void Read(const char* name, int8& v);
	void Read(const char* name, int16& v);
	void Read(const char* name, int64& v);
	void Read(const char* name, std::string& v);

	void Read(int i, int& v);
	void Read(int i, bool& v);
	void Read(int i, float& v);
	void Read(int i, uint8& v);
	void Read(int i, uint16& v);
	void Read(int i, uint32& v);
	void Read(int i, uint64& v);
	void Read(int i, int8& v);
	void Read(int i, int16& v);
	void Read(int i, int64& v);
	void Read(int i, std::string& v);

	Ut::SharedPtr<Json::JObject> m_pData;
	std::vector<Json::JObject*> m_objects;
	std::vector<Json::JArray*> m_arrays;
	std::string m_string;
};

}